package com.ihr360.rest.service;

import com.ihr360.authority.sharing.model.IResourceControl;
import com.ihr360.authority.sharing.privilege.BatchDeletePrivilege;
import com.ihr360.authority.sharing.privilege.DeleteOnePrivilege;
import com.ihr360.authority.sharing.privilege.GetAllPrivilege;
import com.ihr360.authority.sharing.privilege.GetOnePrivilege;
import com.ihr360.authority.sharing.privilege.IAccessControlClass;
import com.ihr360.authority.sharing.privilege.UpdateOnePrivilege;
import com.ihr360.authority.sharing.service.AuthUserResourceService;
import com.ihr360.authority.sharing.util.AccessControlUtils;
import com.ihr360.commons.context.session.Ihr360SessionContextHolder;
import com.ihr360.commons.exception.Ihr360Exception;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.lang.Ihr360BeanUtils;
import com.ihr360.commons.lang.Ihr360ClassUtils;
import com.ihr360.commons.lang.Ihr360EntityUtils;
import com.ihr360.commons.lang.StringUtils;
import com.ihr360.commons.model.type.AccessPrivilege;
import com.ihr360.commons.model.type.ResourcePublicity;
import com.ihr360.event.annonation.RestEventHandler;
import com.ihr360.event.annonation.RestEventListener;
import com.ihr360.rest.reposity.SimpleRestReposity;
import com.ihr360.validation.ValidationUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public abstract class AbstractSimpleRestServiceImpl<T, ID extends Serializable> implements SimpleRestService<T, ID>, ApplicationContextAware {
    private static final String COMPANY_ID = "companyId";
    private final static String ID = "id";

    private static final Logger logger = LoggerFactory.getLogger(AbstractSimpleRestServiceImpl.class);
    private RestEventListener<T, ID> restEventListener;
    private ApplicationContext applicationContext;

    protected AuthUserResourceService authUserResourceService;
    private Class idClass;
    private Class entityClass;
    private Class serviceClass;
    protected IAccessControlClass iAccessControlClass = new IAccessControlClass() {
        @Override
        public Class getServiceClass() {
            return serviceClass;
        }

        @Override
        public Class getEntityClass() {
            return entityClass;
        }

        @Override
        public Class getIdClass() {
            return idClass;
        }
    };

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @PostConstruct
    public void initPostConstruct() {
        restEventListener = getTidRestEventListener();
        authUserResourceService = applicationContext.getBean(AuthUserResourceService.class);
        entityClass = Ihr360ClassUtils.getGenericSuperClassActualTypeArgument(this.getClass(), 0);
        serviceClass = getClass();
        idClass = Ihr360ClassUtils.getGenericSuperClassActualTypeArgument(this.getClass(), 1);
    }

    @Override
    public T getOne(ID id) {
        if (null != restEventListener) {
            restEventListener.beforeGetOne(id);
        }
        T one = getSimpleRestReposity().findOne(id);
        if (IResourceControl.class.isAssignableFrom(entityClass)) {
            GetOnePrivilege getOnePrivilege = new GetOnePrivilege(id, authUserResourceService, iAccessControlClass).doValidate();

            Integer privilege = getOnePrivilege.getPrivilege();
            ((IResourceControl) one).getResourceAccess().setAccessPrivilege(AccessPrivilege.getAccessPrivilege(privilege));

            Integer publicity = getOnePrivilege.getPublicity();
            ((IResourceControl) one).getResourceAccess().setResourcePublicity(ResourcePublicity.getResourcePublicity(publicity));
        }
        if (null != restEventListener) {
            one = restEventListener.afterGetOne(one);
        }

        return one;
    }

    @Override
    public Iterable<T> getAllInCompany() {
        if (null != restEventListener) {
            restEventListener.beforeGetAll();
        }
        Iterable<T> iterable = null;
        try {

            GetAllPrivilege getAllPrivilege = new GetAllPrivilege(authUserResourceService, iAccessControlClass).doValidate();

            if (getAllPrivilege.isAccessControl()) {
                //Is this OK???
                Map<ID, Integer> privilegeMap = getAllPrivilege.getUserResourcesPrivilege();
                Map<ID, Integer> publicityMap = getAllPrivilege.getUserResourcesPublicity();
                if (CollectionUtils.isEmpty(privilegeMap)) {
                    return Collections.emptyList();
                }

                iterable = getSimpleRestReposity().findByIdIn(privilegeMap.keySet());
                if (IResourceControl.class.isAssignableFrom(entityClass)) {
                    iterable.forEach(s -> {
                        BeanWrapper beanWrapper = new BeanWrapperImpl(s);
                        Object objId = beanWrapper.getPropertyValue(ID);
                        Integer privilege = privilegeMap.get(objId);
                        Integer publicity = publicityMap.get(objId);
                        AccessControlUtils.setAccessControl2Target(s, privilege, publicity);
                    });
                }
                ;
            } else {
                String companyId = Ihr360SessionContextHolder.getCompanyId();
                ValidationUtils.rejectIfEmpty(companyId, null, "无法获取公司的有效标识");
                Specification specification = new Specification() {
                    @Override
                    public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
                        return cb.equal(root.get(COMPANY_ID), companyId);
                    }
                };
                iterable = getSimpleRestReposity().findAll(specification);
            }
            if (null != restEventListener) {
                iterable = restEventListener.afterGetAll(iterable);
            }
            return iterable;
        } catch (Exception e) {
            logger.warn(e.getMessage(), e);
        }
        return iterable;
    }


    @Override
    public Iterable<T> getAll() {
        if (null != restEventListener) {
            restEventListener.beforeGetAll();
        }
        Iterable<T> iterable = null;
        try {

            GetAllPrivilege<ID> getAllPrivilege = new GetAllPrivilege<>(authUserResourceService, iAccessControlClass).doValidate();

            if (getAllPrivilege.isAccessControl()) {
                //Is this OK???
                Map<ID, Integer> privilegeMap = getAllPrivilege.getUserResourcesPrivilege();
                Map<ID, Integer> publicityMap = getAllPrivilege.getUserResourcesPublicity();
                if (CollectionUtils.isEmpty(privilegeMap)) {
                    return Collections.emptyList();
                }
                iterable = getSimpleRestReposity().findByIdIn(privilegeMap.keySet());
                if (IResourceControl.class.isAssignableFrom(entityClass)) {
                    iterable.forEach(s -> {
                        BeanWrapper beanWrapper = new BeanWrapperImpl(s);
                        Object objId = beanWrapper.getPropertyValue(ID);
                        Integer privilege = privilegeMap.get(objId);
                        Integer publicity = publicityMap.get(objId);
                        AccessControlUtils.setAccessControl2Target(s, privilege, publicity);
                    });
                }
            } else {
                iterable = getSimpleRestReposity().findAll();
            }
            if (null != restEventListener) {
                iterable = restEventListener.afterGetAll(iterable);
            }
            return iterable;
        } catch (Exception e) {
            logger.warn(e.getMessage(), e);
        }
        return iterable;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchDelete(List<ID> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        if (null != restEventListener) {
            restEventListener.beforeBatchDelete(ids);
        }

        new BatchDeletePrivilege<ID>(ids, authUserResourceService, iAccessControlClass).doValidate();
        getSimpleRestReposity().deleteByIdIn(ids);
        if (null != restEventListener) {
            restEventListener.afterBatchDelete(ids);
        }
    }

    /**
     * 这个功能需要对接数据权限模型？？？
     *
     * @param specification
     * @param pageable
     * @return
     */
    @Override
    public Page<T> search(Specification<T> specification, Pageable pageable, boolean isNoData) {
        if (null != restEventListener) {
            specification = restEventListener.beforeSearch(specification, pageable);
        }
        Page<T> pageData;
        if (!isNoData) {
            pageData = getSimpleRestReposity().findAll(specification, pageable);
        } else {
            pageData = new PageImpl<>(new ArrayList<>(), pageable, 0);
        }
        if (null != restEventListener) {
            pageData = restEventListener.afterSearch(pageData);
        }
        return pageData;
    }

    @Override
    public void deleteOne(ID id) {
        if (null != restEventListener) {
            restEventListener.beforeDeleteOne(id);
        }
        new DeleteOnePrivilege(id, authUserResourceService, iAccessControlClass).doValidate();

        getSimpleRestReposity().delete(id);

        if (null != restEventListener) {
            restEventListener.afterDeleteOne(id);
        }
    }

    @Override
    @Transactional
    public T update(ID id, Map<String, Object> source) {
        T ret = getSimpleRestReposity().findOne(id);

        if (null != restEventListener) {
            restEventListener.beforeUpdate(id, source, ret);
        }
        new UpdateOnePrivilege(id, authUserResourceService, iAccessControlClass).doValidate();

        try {
            Ihr360BeanUtils.copyMapToBean(source, ret);
        } catch (IllegalAccessException e1) {
            logger.warn(e1.getMessage(), e1);
            throw new Ihr360Exception(null, "更新失败");
        } catch (InvocationTargetException e2) {
            logger.warn(e2.getMessage(), e2);
            throw new Ihr360Exception(null, "更新失败");
        } catch (NoSuchMethodException e3) {
            logger.warn(e3.getMessage(), e3);
            throw new Ihr360Exception(null, "更新失败");
        } catch (InstantiationException e4) {
            logger.warn(e4.getMessage(), e4);
            throw new Ihr360Exception(null, "更新失败");
        }

        ret = getSimpleRestReposity().save(ret);
        if (null != restEventListener) {
            restEventListener.afterUpdate(ret);
        }
        return ret;
    }

    /**
     * create应该添加功能权限控制
     *
     * @param t
     * @return
     */
    @Override
    public T create(T t) {
        Ihr360EntityUtils.reinitialize(t);
        if (null != restEventListener) {
            restEventListener.beforeCreate(t);
        }
        T result = getSimpleRestReposity().save(t);
        if (null != restEventListener) {
            restEventListener.afterCreate(t);
        }
        return result;
    }

    /**
     * 创建或更新
     *
     * @param t
     * @return
     */
    @Override
    public T createOrUpdate(ID id, T t) {
        if (null != restEventListener) {
            restEventListener.beforeCreateOrUpdate(id, t);
        }
        if (id != null) {
            new UpdateOnePrivilege(id, authUserResourceService, iAccessControlClass).doValidate();
        }
        T result = getSimpleRestReposity().save(t);
        if (null != restEventListener) {
            restEventListener.afterCreateOrUpdate(result);
        }
        return result;
    }

    private RestEventListener<T, ID> getTidRestEventListener() {
        RestEventHandler annotation = AnnotationUtils.findAnnotation(this.getClass(), RestEventHandler.class);

        RestEventListener<T, ID> handlerBean = null;
        if (null != annotation) {
            Class eventListenerClass = annotation.value();
            if (eventListenerClass.isInstance(RestEventListener.class)) {
                handlerBean = null;
            } else {
                String simpleName = eventListenerClass.getSimpleName();
                String beanName = StringUtils.uncapitalize(simpleName);
                handlerBean = (RestEventListener<T, ID>) applicationContext.getBean(beanName);
            }

        }
        return handlerBean;
    }


    /**
     * 获取具体的SimpleRestReposity的实现
     *
     * @return
     */
    public abstract SimpleRestReposity<T, ID> getSimpleRestReposity();

}